package cn.thoughtworks.school.usercenter.controllers;

import cn.thoughtworks.school.usercenter.entities.*;
import cn.thoughtworks.school.usercenter.entities.commands.CreateUserOrganization;
import cn.thoughtworks.school.usercenter.entities.commands.CreateUsersCommand;
import cn.thoughtworks.school.usercenter.entities.vo.UserInfo;
import cn.thoughtworks.school.usercenter.exceptions.BusinessException;
import cn.thoughtworks.school.usercenter.repositories.*;
import cn.thoughtworks.school.usercenter.services.NodeBBUserService;
import cn.thoughtworks.school.usercenter.services.OrganizationService;
import cn.thoughtworks.school.usercenter.services.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.stream.Collectors;

@Controller
@RequestMapping(value = "/api/users")
public class UserController {

  @Autowired
  private NodeBBUserService nodeBBUserService;

  @Autowired
  private UserDetailRepository userDetailRepository;

  @Autowired
  private UserService userService;

  @Autowired
  private UserRepository userRepository;

  @Autowired
  private NodeBBUserRepository nodeBBUserRepository;
  @Autowired
  private OrganizationService organizationService;
  @Autowired
  private UserRoleRepository userRoleRepository;
  private ObjectMapper oMapper = new ObjectMapper();

  @RequestMapping(value = "/{userId}", method = RequestMethod.GET)
  ResponseEntity getUser(@PathVariable Long userId) throws Exception {
    Map user = userService.findUserById(userId);

    return new ResponseEntity(user, HttpStatus.OK);
  }

  @RequestMapping(value = "/{userId}/nodeBBUser", method = RequestMethod.POST)
  ResponseEntity createNodeBBUser(@PathVariable Long userId) throws Exception {

    NodeBBUser nodeBBUser = nodeBBUserService
      .createNodeBBUserByUserId(userId);
    nodeBBUser.setPassword("");

    return new ResponseEntity(nodeBBUser, HttpStatus.CREATED);
  }

    @PostMapping("import")
    public ResponseEntity importUsers(@RequestBody List<CreateUsersCommand> createUsersCommands) throws BusinessException {
      userService.importUsers(createUsersCommands);

      return new ResponseEntity(HttpStatus.CREATED);
    }
  @RequestMapping(value = "/{userId}/password", method = RequestMethod.PUT)
  ResponseEntity modifyUserPassword(@PathVariable Long userId,
                                    @RequestBody Map<String, String> map)
    throws Exception {
    String encryptedOldPassword = getMD5(getMD5(map.get("oldPassword")));

    User user = userRepository.findById(userId).orElseThrow(() -> new BusinessException("当前用户不存在"));

    if (!encryptedOldPassword.equals(user.getPassword())) {
      throw new BusinessException("旧密码输入有误！");
    }

    String encryptedNewPassword = getMD5(getMD5(map.get("password")));

    user.setPassword(encryptedNewPassword);
    userRepository.save(user);
    return new ResponseEntity(HttpStatus.NO_CONTENT);
  }

  private String getMD5(String str) {
    MessageDigest md = null;
    try {
      md = MessageDigest.getInstance("MD5");
      md.update(str.getBytes());
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    }

    String result = new BigInteger(1, md.digest()).toString(16);
    return result;
  }

  @RequestMapping(value = "/{userId}/detail", method = RequestMethod.PUT)
  ResponseEntity modifyUserDetail(@PathVariable Long userId,
                                  @RequestBody UserInfo userInfo) throws BusinessException {
    userService.modifyUserDetailAndPhone(userInfo, userId);
    return new ResponseEntity(HttpStatus.NO_CONTENT);
  }

  @RequestMapping(value = "/{userId}/nodeBBUser", method = RequestMethod.PUT)
  ResponseEntity updateNodeBBUser(@PathVariable Long userId,
                                  @RequestBody NodeBBUser nodeBBUser) {

    nodeBBUser.setUserId(userId);
    nodeBBUserRepository.save(nodeBBUser);
    return new ResponseEntity(HttpStatus.NO_CONTENT);
  }

  @RequestMapping(value = "/{userId}/apps", method = RequestMethod.GET)
  ResponseEntity getApp(@PathVariable Long userId) throws BusinessException {
    Map result = userService.findApp(userId);
    return new ResponseEntity(result, HttpStatus.OK);
  }

  @RequestMapping(value = "", method = RequestMethod.GET)
  ResponseEntity getUserByUserNameOrEmail(@RequestParam(value =
    "nameOrEmail") String query) {
    Pageable topTen = new PageRequest(0, 10);
    List<User> users = userService.getUserByUsernameContainingOrEmailContaining(query, topTen);

    return new ResponseEntity(users, HttpStatus.OK);
  }

  @RequestMapping(value = "/query", method = RequestMethod.GET)
  ResponseEntity getUsersLikeUsername(@RequestParam(value =
    "username") String query) throws Exception {
    Pageable topTen = new PageRequest(0, 10);
    List<User> users = userRepository
      .getUserByUsernameContaining(query, topTen);
    List userList = new ArrayList<>();
    for (User user : users) {
      Map<String, Object> result = new HashMap();
      result.put("id", user.getId());
      result.put("email", user.getEmail());
      result.put("mobilePhone", user.getMobilePhone());
      result.put("userName", user.getUsername());
      userList.add(result);
    }
    return new ResponseEntity(userList, HttpStatus.OK);
  }

  @RequestMapping(value = "/username", method = RequestMethod.GET)
  ResponseEntity getUserByUsername(@RequestParam(value =
    "username") String username) {
    User user = userService.findUsername(username);

    return new ResponseEntity(user, HttpStatus.OK);
  }

  @RequestMapping(value = "/mobilePhone", method = RequestMethod.GET)
  ResponseEntity findByMobilePhone(@RequestParam(value =
    "mobilePhone") String mobilePhone) {
    User user = userService.findByMobilePhone(mobilePhone);

    return new ResponseEntity(user, HttpStatus.OK);
  }

  @PostMapping(value = "/roles")
  ResponseEntity createUserRole(@RequestBody UserRole userRole) {
    UserRole temp = userRoleRepository.findByOrganizationIdAndUserIdAndRole(userRole.getOrganizationId(),userRole.getUserId(), userRole.getRole());
    if (Objects.isNull(temp)) {
      userRoleRepository.save(userRole);
    }

    return new ResponseEntity(HttpStatus.CREATED);
  }

  @PostMapping(value = "/searches")
  ResponseEntity searchUsersByConditions(@RequestBody Map<String, Object> searchParam) {
    List<String> usernameOrEmail = (List<String>) searchParam.get("usernameOrEmail");
    List<User> users = userRepository.getAllByUsernameInOrEmailIn(usernameOrEmail, usernameOrEmail);
    List<Long> ids = users.stream().map(User::getId).collect(Collectors.toList());
    List<UserDetail> userDetails = userDetailRepository.getAllByUserIdIn(ids);
    List<Map> result = WrappedUser.merge(users, userDetails);

    return new ResponseEntity(result, HttpStatus.CREATED);
  }

  @DeleteMapping(value = "/social/unbinding")
  ResponseEntity unbindingSocialAccount(@RequestParam String username) {
    userService.deleteSocialAccountByUsername(username);

    return new ResponseEntity(HttpStatus.NO_CONTENT);
  }

  @RequestMapping(value = "/ids/{ids}", method = RequestMethod.GET)
  ResponseEntity getUsersByIds(@PathVariable String ids) {
    List<Long> idList = Arrays.stream(ids.split(","))
      .map(Long::parseLong)
      .collect(Collectors.toList());
    List<User> users = userService.findAllByIdIn(idList);

    List<UserDetail> userDetails = userDetailRepository.getAllByUserIdIn(idList);
    List<UserRole> userRoles = userRoleRepository.findByUserIdIn(idList);
    List results = users.stream().map(user -> {
      Map map = oMapper.convertValue(user, Map.class);
      if (userDetails != null) {
        userDetails.stream().forEach(userDetail -> {
          if (userDetail.getUserId().toString().equals(user.getId().toString())) {
            Map detailMap = oMapper.convertValue(userDetail, Map.class);
            map.putAll(detailMap);
          }
        });
      }
      ArrayList<Integer> roles = new ArrayList<>();
      if (userRoles != null) {
        userRoles.stream().forEach(userRole -> {
          if (userRole.getUserId().toString().equals(user.getId().toString())) {
            roles.add(userRole.getRole());
          }
        });
      }
      map.put("roles", roles);
      return map;
    }).collect(Collectors.toList());
    return new ResponseEntity<>(results, HttpStatus.OK);
  }


  @GetMapping("social/account")
  public ResponseEntity getSocialAccount(@RequestParam String username) {
    List<SocialAccount> socialAccounts = userService.findSocialAccountByUsername(username);

    return new ResponseEntity(socialAccounts, HttpStatus.OK);
  }

  @GetMapping("roles")
  public ResponseEntity getRoles() {
    return ResponseEntity.ok(userService.findRoles());
  }

  @PostMapping("{userId}/roles")
  public ResponseEntity addRole(@RequestBody UserRole userRole) {
    userService.addUserRole(userRole);
    return new ResponseEntity(HttpStatus.CREATED);
  }

  @GetMapping("{userId}/organizations")
  public ResponseEntity getOrganizationsById(@PathVariable Long userId) {
    List<Map> organizations = organizationService.getOrganizationsById(userId);
    return ResponseEntity.ok(organizations);
  }

 @PutMapping("organizations")
  public ResponseEntity updateUserCurrentOrganization(@RequestBody CreateUserOrganization createUserOrganization) throws BusinessException {
    userService.updateUserCurrentOrganization(createUserOrganization);
    return new ResponseEntity(HttpStatus.NO_CONTENT);
  }


  @GetMapping("{userId}/roles")
  public ResponseEntity findUserRolesByUserId(@PathVariable Long userId) {
    return ResponseEntity.ok(userService.findUserRolesByUserId(userId));
  }


}
